package com.gccloud.design.core.mp.interceptor;

import cn.hutool.core.util.ReflectUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.gccloud.feign.filter.dto.SysUser;
import com.gccloud.idc.security.shiro.UserUtils;
import com.gccloud.starter.common.constant.GlobalConst;
import com.gccloud.starter.common.exception.GlobalException;
import com.gccloud.starter.common.vo.R;
import com.gccloud.starter.core.dto.SysUserDataPermissionDTO;
import com.gccloud.starter.core.vo.SysUserDataPermissionVO;
import com.gccloud.starter.feign.api.service.ISysUserDataPermissionService;
import com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.sql.Connection;
import java.util.Collection;
import java.util.List;

/**
 * Mybatis 拦截器，用于进行数据权限拦截
 *
 * @author liuchengbiao
 * @date 2020-06-24 17:30
 */
@Slf4j
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataPermissionInteceptor implements Interceptor {

//    @Autowired
//    @Lazy
//    private ISysUserOrgService sysOrgService;
//    @Lazy
    @Autowired
    private ISysUserDataPermissionService sysUserDataPermissionService;

    @Autowired
    private MybatisPlusProperties mybatisPlusProperties;

    private GlobalConfig.DbConfig dbConfig;

//    @Value("${appId}")
    private String appId = "5";

//    @Value("${data-permission}")
    private boolean isDataPermission = true;

    @PostConstruct
    public void init() {
        log.info(GlobalConst.Console.LINE);
        log.info("初始化数据权限拦截器");
        dbConfig = mybatisPlusProperties.getConfiguration().getGlobalConfig().getDbConfig();
        log.info(GlobalConst.Console.LINE);
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (isDataPermission) {
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            // 通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性;：MetaObject是Mybatis提供的一个用于方便、
            // 优雅访问对象属性的对象，通过它可以简化代码、不需要try/catch各种reflect异常，同时它支持对JavaBean、Collection、Map三种类型对象的操作。
            MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
            // 先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
            // id为执行的mapper方法的全路径名，如 com.gccloud.starter.core.dao.BaseDao.deleteByIdWithDp
            String id = mappedStatement.getId();
            if (!id.endsWith("WithDp")) {
                return invocation.proceed();
            }
            BoundSql boundSql = statementHandler.getBoundSql();
            //获取到原始sql语句
            String sql = boundSql.getSql();
            log.info("执行拦截器：{}", sql);
            SysUser user = UserUtils.getCurrentUser();
            if (user == null) {
                throw new GlobalException(String.format("用户未登录，不可调用该方法：%s", id));
            }
            // 根据用户id和应用id查询用户权限
            SysUserDataPermissionDTO dto = new SysUserDataPermissionDTO();
            dto.setUserId(user.getId());
            dto.setAppId(appId);
            R<SysUserDataPermissionVO> sysUserDataPermissionVO = sysUserDataPermissionService.getDataPermissionByUserIdAndAppId(dto);
            if (sysUserDataPermissionVO == null || sysUserDataPermissionVO.getData() == null || StringUtil.isBlank(sysUserDataPermissionVO.getData().getDataPermission())) {
                throw new GlobalException(String.format("未获取到用户权限，不可调用该方法：%s", id));
            }

//        int dataPermission = user.getDataPermission();

            SysUserDataPermissionVO sysUserDataPermission = sysUserDataPermissionVO.getData();
            List<String> listOrg = sysUserDataPermission.getOrgIdList();
            String dataPermission = sysUserDataPermission.getDataPermission();

            if (String.valueOf(GlobalConst.User.DataPermission.USER).equals(dataPermission)) {
                sql = sql.replace("WHERE", " WHERE " + String.format(" %s = %s AND ", GlobalConst.ColumnField.CREATE_BY, parseSql(user.getId())));
            } else if (String.valueOf(GlobalConst.User.DataPermission.ORG).equals(dataPermission)) {
                sql = sql.replace("WHERE", " WHERE " + String.format(" %s = %s AND ", GlobalConst.ColumnField.ORG_ID, parseSql(user.getOrgId())));
            } else if (String.valueOf(GlobalConst.User.DataPermission.ALL).equals(dataPermission)) {
                // 查询全部数据
            } else if (String.valueOf(GlobalConst.User.DataPermission.ORG_AND_SON).equals(dataPermission)) {
                sql = sql.replace("WHERE", " WHERE " + String.format(" %s in (%s) AND ", GlobalConst.ColumnField.ORG_ID, parseSql(listOrg)));
            } else if (String.valueOf(GlobalConst.User.DataPermission.ORG_CUSTOM).equals(dataPermission)) {
                // 自定义机构
                sql = sql.replace("WHERE", " WHERE " + String.format(" %s in (%s) AND ", GlobalConst.ColumnField.ORG_ID, parseSql(listOrg)));
            } else {
                throw new GlobalException(String.format("不支持数据权限类型: %s", dataPermission));
            }

            ReflectUtil.setFieldValue(boundSql, "sql", sql);
            return invocation.proceed();
        }
        return invocation.proceed();
    }

    private String parseSql(String columnValue) {
        IdType idType = dbConfig.getIdType();
        if (idType == IdType.AUTO) {
            // 自增
            return columnValue;
        }
        return "\'" + columnValue + "\'";
    }

    private String parseSql(Collection<String> columnValueSet) {
        IdType idType = dbConfig.getIdType();
        if (idType == IdType.AUTO) {
            // 自增
            return Joiner.on(",").join(columnValueSet);
        }
        StringBuffer sb = new StringBuffer();
        for (String column : columnValueSet) {
            sb.append("\'").append(column).append("\'").append(",");
        }
        return sb.delete(sb.length() - 1, sb.length()).toString();
    }
}
